package sg.edu.nus.iss.vmcs.payment;

import java.util.ArrayList;

import sg.edu.nus.iss.vmcs.store.CashStore;
import sg.edu.nus.iss.vmcs.store.Coin;
import sg.edu.nus.iss.vmcs.store.Store;
import sg.edu.nus.iss.vmcs.store.StoreController;
import sg.edu.nus.iss.vmcs.util.Log;
import sg.edu.nus.iss.vmcs.util.VMCSException;

import java.util.Observable;

import sg.edu.nus.iss.vmcs.system.Fault;
import sg.edu.nus.iss.vmcs.system.FaultObserver;
import sg.edu.nus.iss.vmcs.system.FaultType;

/**
 * @author moe Coin Receiver Implement the Observer Pattern for Fault
 */
public class CoinReceiver implements FaultObserver {
	private ArrayList<Coin> coins;
	private int totalInserted;

	private CoinPayment ctrl;
	private CoinInputBox coinInputBox;

	private Fault fault = null;

	private CoinReceiver() {
		observeFault(Fault.getInstance());// SelfRegister to Fault
	}

	/**
	 * Observe the Fault
	 * 
	 * @param fault
	 */
	public void observeFault(Fault fault) {
		Log.LogIt("CoinReceiver.ObserveFault" + fault.getState());
		fault.addObserver(this);
		this.fault = fault;
	}

	/**
	 * update the CoinReceiver according to the fault
	 */
	// public void update(Fault o, FaultType arg) {
	public void update(Observable o, Object arg) {
		Log.LogIt("CoinReceiver.update");
		boolean active = false;
		FaultType faultType = ((Fault) o).getState();
		Log.LogIt("CoinReceiver.update(" + faultType + ")");
		switch (faultType) {
		case CoinReceiverFault:
			// Nothing to do
			break;
		case DispenseFault:
			// Disable the CoinReceiver
			break;
		case NO_FAULT:
			// Enable the CoinReceiver
			active = true;
			break;
		default:
			// Disable the CoinReceiver
			active = false;
			break;
		}
		setActive(false);
		refundCash(false);
		Log.LogIt("CoinReceiver.update:setActive(" + active + ")");
	}

	public CoinReceiver(CoinPayment coinPayment) {
		this();
		this.ctrl = coinPayment;
		totalInserted = 0;
		coins = new ArrayList<Coin>(10); // Assume an initial capacity of 10
		// Coin elements is required
	}

	/*
	 * Start Receiving the coins CoinInputBox and updated the
	 * total_money_inserted_display in the CustomerPanel
	 */
	public void startReceive() {
		setActive(true);
		totalInserted = 0;
		coins.clear();
	}

	public void receiveCoin(double weight) {
		ctrl.getPanel().setStateInvalidCoinDisplay(false);
		ctrl.getPanel().setValueRefundChangeTrayDisplay("0");
		ctrl.getPanel().getCoinInputBox().setActive(false);
		Coin foundCoin = ((CashStore) getStoreController().getStore(
				Store.CASH)).findCoin(weight);
		if (foundCoin != null) {
			totalInserted = totalInserted + foundCoin.getValue();
			coins.add(foundCoin);
			ctrl.processReceive(totalInserted);
			ctrl.getPanel().setValueTotalMoneyInsertedDisplay(
					totalInserted);
		} else {
			ctrl.getPanel().setStateInvalidCoinDisplay(true);
			ctrl.getPanel().setValueRefundChangeTrayDisplay(
					"INVALID COIN");
			ctrl.getPanel().getCoinInputBox().setActive(true);
		}
		
	}

	public void continueReceive() {
		setActive(true);
	}

	public boolean storeCash() throws VMCSException {
		boolean success = false;
		while (coins.size() > 0) {
			Coin aCoin = coins.get(0);
			try {
				success = ctrl.getMainController().getMachineryController()
						.storeCoin(aCoin);
			} catch (VMCSException ex) {
				throw new VMCSException("CoinReceiver.storeCash", ex
						.getMessage());
			}
			coins.remove(0);
		}
		ctrl.getPanel().setValueTotalMoneyInsertedDisplay(0);
		return success;
	}

	public void stopReceive() {
		setActive(false);
	}

	public void refundCash(Boolean clearWarning) {
		if (totalInserted == 0) {
			ctrl.getPanel().setValueRefundChangeTrayDisplay(
					String.valueOf(totalInserted));
			coins.clear();
		} else {
			for (int i = 0; i < coins.size(); i++) {
				ctrl.getPanel().setValueRefundChangeTrayDisplay(
						String.valueOf(totalInserted));
				ctrl.getPanel().setValueTotalMoneyInsertedDisplay(0);
				if (clearWarning) {
					ctrl.getPanel()
							.setStateInvalidCoinDisplay(false);
				}
				totalInserted = 0;
				coins.clear();
			}
		}
	}

	public void setActive(boolean active) {
		ctrl.getPanel().getCoinInputBox().setActive(active);
	}
	
	public StoreController getStoreController(){
		return ctrl.getMainController().getStoreController();
	}

	/**
	 * Set Error status of Coin Receiver
	 * 
	 * @param err
	 *            <p> True - Coin Receiver Has Error 
	 *            <p> False - Coin Receiver Error was cleared
	 */
	public void setError(boolean err) {
		if (err) {
			fault.setState(FaultType.CoinReceiverFault);
		} else {
//			if (fault.getState().compareTo(FaultType.CoinReceiverFault) == 0) {
//				// Current Fault is Coin Receiver Fault. Reset it.
//				fault.setState(FaultType.NO_FAULT);
//			}
			fault.clearFault(FaultType.CoinReceiverFault);
		}

		coinInputBox = ctrl.getPanel().getCoinInputBox();
		coinInputBox.setProblemButton(err);
		coinInputBox.setActive(false);
	}

	private Fault getFault() {
		if (fault == null)
			fault = Fault.getInstance();
		return fault;
	}

	/**
	 * @param fault
	 *            - the fault to set
	 */
	public void setFault(Fault fault) {
		this.fault = fault;
	}
}
